/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		ploter.hpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date: 2020-02-29 01:51
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-03-21 16:17
*
*   Description:
*
=============================================================================*/
#if !defined(DATA_PLOTER_FOR_ENTITIES)
#define DATA_PLOTER_FOR_ENTITIES

#include "../external/gnuplot-iostream.h"
#include "../types.hpp"
#include "../util/regex.hpp"
#include <tuple>
#include <unordered_map>

namespace geoxfem {
namespace io {

enum class PlotType {
  gauss,
  free_BC,
  u0_BC,
  u1_BC,
  u2_BC,
  u0u1_BC,
  u0u2_BC,
  u1u2_BC,
  u0u1u2_BC,
  pore_BC,
  flow_BC,
  line,
  force_BC
};

struct DataPloter {
  /// default constructor
  DataPloter() = default;
  /// default destructor
  ~DataPloter() {
    show();
  }

  /// get class name
  std::string getClassName() const {
    return "DataPloter";
  }

  void appendSetting(const std::string& _str) {
    m_settingCMD.append(_str + "\n");
    return;
  }
  void appendPlotCmd(const std::string& _str) {
    m_plotCMD.append(_str + "\n");
    return;
  }
  void appendData(
    std::vector<std::tuple<_real, _real, _real, _real, _real, _real>>& input) {
    m_data.emplace_back(input);
  }

  void plotPoint(const _real&       _x,
                 const _real&       _y,
                 const _real&       _z,
                 const std::string& _setting = " w p pt 6 ",
                 const std::string& _title   = " notitle ");
  void plotLine(const _real&       _x0,
                const _real&       _y0,
                const _real&       _z0,
                const _real&       _x1,
                const _real&       _y1,
                const _real&       _z1,
                const std::string& _setting = " w l ls -1 ",
                const std::string& _title   = " notitle ");
  void plotVec(const _real&       _x,
               const _real&       _y,
               const _real&       _z,
               const _real&       _dx,
               const _real&       _dy,
               const _real&       _dz,
               const std::string& _setting = " with vectors",
               const std::string& _title   = " notitle ");
  void plotPoint(const PlotType&    _type,
                 const _real&       _x,
                 const _real&       _y,
                 const _real&       _z,
                 const _real&       _dx    = 0,
                 const _real&       _dy    = 0,
                 const _real&       _dz    = 0,
                 const std::string& _title = " notitle ");

  void plotContour(
    const std::vector<std::tuple<_real, _real, _real, _real, _real, _real>>&
                       _data,
    const std::string& _title = std::string());

  void plotLegend(const std::string& _setting, const std::string& _title);
  void plotLegend(const PlotType& _type);
  void print() {
    auto [if_match, not_use] =
      util::parseName<util::ignore_case>(m_plotCMD, "splot.*");
    if(!if_match)
      m_plotCMD.insert(0, "splot ");

    std::cout << m_settingCMD << m_plotCMD << "\n";
    return;
  }
  void show();

private:
  /// ploter
  gnuplotio::Gnuplot m_gp;
  /// string store _setting command
  std::string m_settingCMD;
  /// string store ploting command
  std::string m_plotCMD;
  ///
  std::vector<std::vector<std::tuple<_real, _real, _real, _real, _real, _real>>>
    m_data;
  ///
  static std::pair<std::string, std::string>
  getConfiguration(const PlotType& _type) { /*{{{*/
    static std::unordered_map<PlotType, std::pair<std::string, std::string>>
      plot_configuration = {
        {PlotType::gauss, {" w p pt 1 lc \'red\'", " title \"gauss point\""}},
        {PlotType::free_BC, {" w p pt -1 ", " title \"free node\""}},
        {PlotType::u0_BC,
         {" w p pt 8 lc \'red\'", " title \"$\\delta{u_0}=const$\""}},
        {PlotType::u1_BC,
         {" w p pt 8 lc \'green\'", " title \"$\\delta{u_1}=const$\""}},
        {PlotType::u2_BC,
         {" w p pt 8 lc \'blue\'", " title \"$\\delta{u_2}=const$\""}},
        {PlotType::u0u1_BC,
         {" w p pt 9 lc \'yellow\'",
          " title \"$\\delta{u_0}=const \\delta{u_1}=const$\""}},
        {PlotType::u0u2_BC,
         {" w p pt 9 lc \'pink\'",
          " title \"$\\delta{u_0}=const \\delta{u_2}=const$\""}},
        {PlotType::u1u2_BC,
         {" w p pt 9 lc \'cyan\'",
          " title \"$\\delta{u_1}=const \\delta{u_2}=const$\""}},
        {PlotType::u0u1u2_BC,
         {" w p pt 9 lc \'black\'",
          " title \"$\\delta{u_0}=const \\delta{u_1}=const "
          "\\delta{u_2}=const$\""}},
        {PlotType::pore_BC,
         {" w p pt 9 lc \'dark-yellow\'", " title \"$\\delta{pore}=const$\""}},
        {PlotType::flow_BC,
         {" w p pt 6 lc \'dark-yellow\'", " title \"$\\delta{flow}!=0$\""}},
        {PlotType::line, {" w l ls -1 ", " notitle "}},
        {PlotType::force_BC, {" with vectors lc \'red\'", " title \"force\""}}};
    return plot_configuration.at(_type);
  }
}; /*}}}*/

void DataPloter::plotPoint(const _real&       _x, /*{{{*/
                           const _real&       _y,
                           const _real&       _z,
                           const std::string& _setting,
                           const std::string& _title) {
  /* std::vector<std::tuple<_real, _real, _real, _real, _real, _real>> pt_data;
   */
  m_plotCMD.append(" \'-\'" + _setting + _title + ",");
  /* pt_data.push_back(std::make_tuple(_x, _y, _z, r0, r0, r0)); */
  m_data.push_back({{_x, _y, _z, r0, r0, r0}});
  return;
} /*}}}*/
void DataPloter::plotPoint(const PlotType&    _type, /*{{{*/
                           const _real&       _x,
                           const _real&       _y,
                           const _real&       _z,
                           const _real&       _dx,
                           const _real&       _dy,
                           const _real&       _dz,
                           const std::string& _title) {
  /* std::vector<std::tuple<_real, _real, _real, _real, _real, _real>> _data; */
  std::string _setting = DataPloter::getConfiguration(_type).first;
  m_plotCMD.append(" \'-\'" + _setting + _title + ",");
  /* _data.push_back(std::make_tuple(_x - _dx, _y - _dy, _z - _dz, _dx, _dy,
   * _dz)); */
  m_data.push_back({{_x - _dx, _y - _dy, _z - _dz, _dx, _dy, _dz}});
  return;
} /*}}}*/
void DataPloter::plotLine(const _real&       _x0, /*{{{*/
                          const _real&       _y0,
                          const _real&       _z0,
                          const _real&       _x1,
                          const _real&       _y1,
                          const _real&       _z1,
                          const std::string& _setting,
                          const std::string& _title) {
  /* std::vector<std::tuple<_real, _real, _real, _real, _real, _real>>
   * line_data; */
  m_plotCMD.append(" \'-\'" + _setting + _title + ",");
  /* line_data.push_back(std::make_tuple(_x0, _y0, _z0, r0, r0, r0)); */
  /* line_data.push_back(std::make_tuple(_x1, _y1, _z1, r0, r0, r0)); */
  m_data.push_back({{_x0, _y0, _z0, r0, r0, r0}, {_x1, _y1, _z1, r0, r0, r0}});
  return;
} /*}}}*/
void DataPloter::plotVec(const _real&       _x, /*{{{*/
                         const _real&       _y,
                         const _real&       _z,
                         const _real&       _dx,
                         const _real&       _dy,
                         const _real&       _dz,
                         const std::string& _setting,
                         const std::string& _title) {
  std::vector<std::tuple<_real, _real, _real, _real, _real, _real>> vec_data;
  m_plotCMD.append(" \'-\':4:5:6" + _setting + _title + ",");
  vec_data.push_back(std::make_tuple(_x, _y, _z, _dx, _dy, _dz));
  m_data.push_back(vec_data);
  return;
} /*}}}*/
///----------------------------------------------------------------------------

void DataPloter::
  plotContour(/*{{{*/
              const std::vector<
                std::tuple<_real, _real, _real, _real, _real, _real>>& _data,
              const std::string&                                       _title) {
  appendSetting("set dgrid3d 50,50,20 spline");
  appendSetting("set contour base");
  appendSetting("unset surface");

  if(!_title.empty())
    appendSetting("set title \"" + _title + "\"");

  m_plotCMD.append("\'-\' using 1:2:3 w l,");
  m_data.emplace_back(_data);
  return;
} /*}}}*/
///----------------------------------------------------------------------------

void DataPloter::plotLegend(const std::string& _setting, /*{{{*/
                            const std::string& _title) {
  m_plotCMD.append(" NaN " + _setting + _title + ",");
  return;
} /*}}}*/
///----------------------------------------------------------------------------

void DataPloter::plotLegend(const PlotType& _type) { /*{{{*/
  const std::string _setting = DataPloter::getConfiguration(_type).first;
  const std::string _title   = DataPloter::getConfiguration(_type).second;
  m_plotCMD.append(" NaN " + _setting + _title + ",");
  return;
} /*}}}*/
///----------------------------------------------------------------------------
void DataPloter::show() { /*{{{*/
  m_gp << m_settingCMD;
  auto [if_match, not_use] =
    util::parseName<util::ignore_case>(m_plotCMD, "splot.*");
  if(!if_match)
    m_plotCMD.insert(0, "splot ");
  m_gp << m_plotCMD << "\n";
  for(auto& _data : m_data)
    m_gp.send1d(_data);
  return;
} /*}}}*/

}  // namespace io
}  // namespace geoxfem
#endif  // _DATA_PLOTER_FOR_ENTITIES
